home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Reference / Amiga_Mail_Vol2 / Archives / Plain / ja92 / Optim / Optim.txt < prev    next >
Encoding:
Text File  |  1992-08-18  |  24.8 KB  |  508 lines

  1. (c)  Copyright 1992 Commodore-Amiga, Inc.   All rights reserved.
  2. The information contained herein is subject to change without notice,
  3. and is provided "as is" without warranty of any kind, either expressed
  4. or implied.  The entire risk as to the use of this information is
  5. assumed by the user.
  6.  
  7.  
  8.  
  9. Optimized Window Refreshing
  10.  
  11.  
  12. by Martin Taillefer
  13.  
  14.  
  15. Maintaining the graphical contents of an Amiga window can be
  16. difficult.  There are many subtleties associated with the process
  17. known as window refreshing.  At present, many applications refresh
  18. their windows in suboptimal ways, or fail to refresh correctly under
  19. all conditions.  This article attempts to explore and resolve the
  20. window refreshing problems commonly encountered by applications.
  21.  
  22.  
  23. Damaging Information
  24.  
  25. Although many people think of Intuition as the Amiga's windowing
  26. system, the lower-level Layers library actually handles most of the
  27. work involved in maintaining Intuition's windowed environment.  The
  28. Layers library divides a single physical display (a BitMap) into
  29. multiple virtual displays.  Each of these virtual displays is known
  30. as a layer.  Intuition uses the functions in the Layers library to
  31. move, resize, and depth arrange these layers.
  32.  
  33. Normally, each Intuition window consists of a single layer.
  34. Intuition adds borders and gadgetry to the layer to give it the
  35. familiar Intuition window appearance.  Intuition also takes care of
  36. monitoring user input to let the user move, resize, and depth arrange
  37. windows.  When Intuition wishes to change the size or position of a
  38. window, it calls functions in the Layers library to do most of the
  39. grunt work.
  40.  
  41. One of the main reasons for a layered rendering system is to provide
  42. multiple independent logical rendering regions to applications.  The
  43. layer is the basis for the Intuition Window.  The functions in the
  44. Layers library allow several applications to render into the same
  45. physical display (an Intuition Screen for example) without worrying
  46. about interfering with each other.  As far as the application is
  47. concerned, it has an entire display all to itself.
  48.  
  49. There is a limit to the isolation that this layered environment
  50. offers applications.  Because layers can overlap, by moving,
  51. resizing, or deleting a layer, a program can uncover portions of
  52. underlying layers.  These newly exposed portions are called ``damage
  53. regions''.  A layer can sustain damage when a task performs layers
  54. operations on it or other layers in the same display.  When the
  55. Layers library damages a layer, it sets the layer's LAYERREFRESH bit
  56. in the Flags field of the Layer structure.
  57.  
  58. When damage occurs to a layer, the damaged region of the layer must
  59. be repaired by redrawing it.  The entity responsible for redrawing
  60. the damaged region depends on the type of layer.  The Layers library
  61. offers three types of layers: simple refresh, smart refresh, and
  62. superbitmap.  Subsequently, Intuition bases its three types of
  63. windows on these three types of layers.  The difference between each
  64. type has to do with the way in which each handles damage repair.
  65.  
  66. When a layers operation damages a simple refresh layer, the entire
  67. burden of repairing the layer's damage rests on the application that
  68. created the layer (if the application is using Intuition windows,
  69. which are built on top of layers, Intuition shares the burden of
  70. damage repair with the application.  More on this later).  This is
  71. because a simple refresh layer does not preserve its contents.  The
  72. advantage of this type of layer is that it doesn't use much memory.
  73. The disadvantage is that every time a layers operation reveals a
  74. portion of a simple refresh layer, the application must explicitly
  75. rerender the exposed damaged regions.
  76.  
  77. Smart refresh layers help the application by doing some of the
  78. refreshing automatically.  If a layer operation conceals a portion of
  79. a layer, that portion is automatically copied to an off-screen
  80. buffer.  If a layer operation later reveals that portion of the
  81. layer, the Layers library uses the temporary buffer to update the
  82. revealed region.  The Layers library does not leave the LAYERREFRESH
  83. bit set in this case because the Layers library took care of the
  84. damage.  The application still needs to refresh the smart refresh
  85. layer whenever it is made larger, as the Layers library has no idea
  86. what should be in the newly exposed areas.  In this case, the Layers
  87. library will leave the LAYERREFRESH bit set.
  88.  
  89. Finally, superbitmap layers totally eliminate the need for an
  90. application to refresh the display.  The Layers library maintains a
  91. complete off-screen buffer representing the layer's contents.  When a
  92. layer operation exposes new portions of a layer, the Layers library
  93. automatically updates these regions by copying from the off-screen
  94. buffer.  The Layers library will never leave the LAYERREFRESH bit set
  95. for a superbitmap layer.
  96.  
  97. For the application programmer, a superbitmap layer offers the
  98. simplest approach to refreshing the layer as the entire burden of
  99. repairing damaged regions falls on the Layers library.  This added
  100. convenience does have a cost--because of its off-screen buffer, the
  101. superbitmap layer requires a significantly larger amount of memory
  102. compared to the simple and smart refresh layers.  The Layers library
  103. allocates this memory even if the layer never sustains damage.
  104.  
  105.  
  106. When To Refresh
  107.  
  108. The way in which an application determines that it needs to refresh a
  109. layer depends on whether an application deals with layers directly
  110. via the Layers library, or indirectly via Intuition.  It is important
  111. to keep in mind that each Intuition window has a Layer at its core.
  112. If an application uses Intuition windows it may not use the Layers
  113. library to create, delete, move, resize, or update the window layers.
  114. The application must use the corresponding Intuition functions
  115. instead.
  116.  
  117. If an application uses layers directly, it must look at a layer's
  118. LAYERREFRESH bit to tell if the layer sustained damage and needs
  119. repair.  Since the application created and maintains its layers, it
  120. knows when damage can occur, so it checks for damage at those times.
  121.  
  122. Intuition manages an arbitrary number of layers on behalf of any
  123. number of client applications.  All windowing operations happen on
  124. Intuition's time frame.  Since other applications and the user can
  125. ask Intuition to manipulate window layers around at any time, it is
  126. not possible for a window-based application to know by itself when to
  127. refresh its window.  That would involve polling the window layer's
  128. LAYERREFRESH bit, which is a big no-no in a multitasking system.
  129. Instead, Intuition provides a refresh notification mechanism through
  130. the IDCMP system.
  131.  
  132. Whenever Intuition performs a layer operation that can damage a
  133. window layer, it checks the damage state of each layer in the current
  134. screen by inspecting each layer's LAYERREFRESH bit.  If Intuition
  135. finds that a layer's LAYERREFRESH bit is set, Intuition takes care of
  136. refreshing the damaged areas of the window that are Intuition's
  137. responsibility (for example, the window borders and gadgets).
  138. Following that, Intuition looks at the WFLG_NOCAREREFRESH bit in the
  139. Window structure.  If this bit is set, Intuition's refresh processing
  140. for the window is complete, and all damage region information is
  141. discarded.  However, If the bit is clear, Intuition sends an
  142. IDCMP_REFRESHWINDOW message to the window's IDCMP port, essentially
  143. asking the window to refresh itself.
  144.  
  145. So, instead of having to poll the LAYERREFRESH bit, an application
  146. can just wait for Intuition to tell it to refresh its window.  This
  147. is convenient and fits in well with the IDCMP mechanism.
  148.  
  149.  
  150. Scrolling Your Life Away
  151.  
  152. A scrolling display is a part of many user interfaces.  An easy way
  153. to make people think an application is slow is to give it a sluggish
  154. user interface.  Regardless of the actual speed at which an
  155. application performs its job, if its user interface management is
  156. slow, the user will get the impression the whole application is slow.
  157.  
  158. The ScrollRaster() and ClipBlit() routines are the two principal ways
  159. of scrolling data within a layer.  ScrollRaster() moves the data
  160. within a RastPort.  ClipBlit() moves data from one RastPort to the
  161. next, but works equally well if the source and destination RastPorts
  162. are the same.
  163.  
  164. Smart refresh layers are relatively easy to scroll with either
  165. ScrollRaster() or ClipBlit().  Although ClipBlit() has a theoretical
  166. advantage over ScrollRaster(), the performance of these functions is
  167. generally equivalent.  ScrollRaster() does a minimum of two Blitter
  168. operations: one to scroll the data, and a generally smaller one to
  169. erase the area that was scrolled away.  ClipBlit() can do a single
  170. blit to move the data in the layer, and leaves the rest of the
  171. display alone.  Thus, while scrolling large amounts of data, an
  172. application can get better CPU/Blitter processing overlap by using
  173. ClipBlit().  Consider the following pseudo-code of what happens in
  174. ScrollRaster() versus ClipBlit():
  175.  
  176. Within the ScrollRaster() function:
  177.   Wait for any outstanding Blitter operation to complete
  178.   Initiate a Blitter operation to move the data in the window
  179.   Wait for the Blitter operation to complete
  180.   Initiate a second small Blitter operation to clear the scrolled region
  181.   Return to the caller while the small blit is still in progress
  182.  
  183. Within the ClipBlit() function:
  184.   Wait for any outstanding Blitter operation to complete
  185.   Initiate a Blitter operation to move the data in the window
  186.   Return to the caller while the blit is still in progress
  187.  
  188. Since the Blitter and the CPU can run concurrently, the second
  189. approach provides the most throughput, as the Blitter performs most
  190. of the scrolling work at the same time the application code runs
  191. after ClipBlit().  In practice though, ScrollRaster() and ClipBlit()
  192. run at about the same speed.
  193.  
  194. For a smart refresh window, ScrollRaster() and ClipBlit() do not
  195. present a problem.  As long as the application only scrolls the
  196. contents of the window and not the window borders, the Layers library
  197. will take care of fixing any damage.  Intuition will never know what
  198. happened.  For a simple refresh window however, there is a problem.
  199. The reason has to do with the following:
  200.  
  201.    +--------------+           +--------------+
  202.    |aaaaaaaaaaaaaa|           |bbbbaaaaaabbbb|
  203.    |bbbb+----+bbbb|           |cccc+----+cccc|
  204.    |cccc|    |cccc|           |dddd|    |dddd|
  205.    |dddd+----+dddd|           |eeee+----+eeee|
  206.    |eeeeeeeeeeeeee|           |              |
  207.    +--------------+           +--------------+
  208.  
  209.       Figure  1a                 Figure  1b
  210.  
  211. Figure 1a represents a small window that the user placed on top of a
  212. larger simple refresh window.  When an application scrolls the larger
  213. window's contents upwards with ScrollRaster(), the result looks like
  214. Figure 1b.  This operation exposes two portions of the larger window,
  215. the section at the bottom of the large window (the part
  216. ScrollRaster() clears) and also the area above the smaller window
  217. (which ScrollRaster() leaves intact).
  218.  
  219. The application that owns the larger window has to refresh the two
  220. portions exposed by ScrollRaster().  The application knows to refresh
  221. the bottom section of the window because the damage is a direct
  222. result of the application calling ScrollRaster().  The problem is the
  223. area above the smaller window.  The application does not know that
  224. there is a window overlapping its larger window, so it does not
  225. directly know about any damage resulting from other overlapping
  226. layers.  Because the application used a graphics.library function to
  227. manipulate the window, Intuition does not know about the damage
  228. either.  The result is that the larger window is damaged and no one
  229. knows it.
  230.  
  231. If the larger window had been a smart refresh window, the Layers
  232. library would have cached this portion of the larger window and
  233. ScrollRaster() would have taken action to make sure the cached region
  234. got restored.  For a simple refresh window, this portion only gets
  235. added to the window layer's damage list and no rendering actually
  236. occurs.
  237.  
  238. The solution to this problem is for the application to act as
  239. Intuition would if it were scrolling.  When Intuition manipulates a
  240. layer, it checks the LAYERREFRESH bit of each of its layers to see if
  241. any of them was damaged.  After the application calls ScrollRaster()
  242. on its window, the application has to look at the LAYERREFRESH bit of
  243. the window's layer.  If the bit is set, damage exists and the
  244. application needs to repair the window.  As this window is the only
  245. window that can sustain layer damage as a result of the call to
  246. ScrollRaster(), the application needs to check only its own window's
  247. layer for damage.
  248.  
  249. Note that to scroll the contents of a simple refresh window, an
  250. application has to use ScrollRaster() rather than ClipBlit().  The
  251. reason is ClipBlit() does not add anything to the window layer's
  252. damage region, so an application will never know about the damage.
  253. Also, unlike ScrollRaster(), ClipBlit() does not scroll the window
  254. layer's damage region.  If the layer already has damage when an
  255. application calls ScrollRaster(), the position of the damage region
  256. will move along with the data in the layer.  Note that prior to
  257. Release 2, ScrollRaster() did not scroll the layer's damage list.
  258.  
  259.  
  260. Faster Rendering
  261.  
  262. When rendering to a RastPort, an application can improve its
  263. rendering performance if it renders using a limited number of colors.
  264. Each RastPort contains a mask field which specifies the writeable
  265. bitplanes of the RastPort's BitMap.  If a RastPort's bitplane is
  266. write-protected, the system ignores that bitplane when rendering to
  267. the RastPort.
  268.  
  269. An application can control the BitMap mask by using the SetWrMsk()
  270. macro (defined in <graphics/gfxmacros.h>).  This macro accepts a
  271. pointer to a RastPort, and a new mask.  The mask is an 8 bit value.
  272. Each bit of the value represents a BitMap bitplane.  For example, a
  273. mask of value 5, which is 00000101 in binary, restricts rendering in
  274. that BitMap to planes 0 and 2 which are the only two bits set in the
  275. binary value.
  276.  
  277. One type of application that can improve its performance by using the
  278. RastPort Mask is the text editor.  Typically, a text editor needs to
  279. render its text in a single color, generally color 1.  This means the
  280. editor only needs to render to bitplane 0.  All other planes will
  281. always remain blank.  If these planes are going to remain blank, why
  282. should the editor bother to render to them or scroll them?
  283.  
  284. When scrolling large sections of the display, ScrollRaster() can make
  285. the display look unstable because it continuously clears portions of
  286. the display.  ClipBlit() eliminates this visual nuisance as it does
  287. not clear the display.  Unfortunately, ClipBlit() by itself is
  288. useless for scrolling a simple refresh window because it does not
  289. deal with damage regions.  The solution is sneaky but quite
  290. effective.  An application can do the following each time it scrolls
  291. a simple refresh layer:
  292.  
  293.   Set the RastPort Mask to limit the number of writeable bitplanes.
  294.   Use ClipBlit() to scroll the data.
  295.   Set the RastPort Mask to 0.
  296.   Use ScrollRaster() to scroll the same data.
  297.  
  298. In the procedure above, ClipBlit() scrolls the window contents but
  299. not the damage regions.  ScrollRaster() scrolls the damage region,
  300. but because the RastPort mask is 0, ScrollRaster() does not affect
  301. the window contents.  Setting the RastPort mask to 0 prevents the
  302. system from disturbing the data in any of the planes of the BitMap.
  303.  
  304. The above trick can come in quite handy.  It is fairly fast as well,
  305. although it can involve some hidden overhead.  Even though
  306. ScrollRaster() doesn't move data on the display, it still needs to go
  307. through all the layer's clipping regions, which can be time consuming.
  308.  
  309.  
  310. Using Multiple RastPorts
  311.  
  312. A RastPort specifies attributes needed to perform many rendering
  313. operations.  These include the RastPort's foreground, background, and
  314. drawing mode.  The Graphics library functions SetAPen(), SetBPen(),
  315. and SetDrMd() set each of these attributes, respectively.
  316.  
  317. Although these functions appear to have fairly simple purposes in
  318. life, the SetAPen(), SetBPen(), and SetDrMd() are quite CPU intensive
  319. routines.  These functions require recalculating values that the OS
  320. caches in private parts of the RastPort.  If an application only
  321. requires a few different combinations of foreground/background/draw
  322. mode, it can improve its performance by using a different RastPort
  323. structure for each combination.  An application sets the attributes
  324. of several RastPorts only once which is generally more efficient that
  325. setting the attributes of one RastPort every time the rendering
  326. attributes change.
  327.  
  328. Assume an application has a window in which it renders all of its
  329. data in pen 1, and clears any part of its display using color 0.
  330. Such an application can improve its rendering performance by doing
  331. the following:
  332.  
  333.     struct RastPort dataRP;
  334.     struct RastPort clrRP;
  335.  
  336.     dataRP = *window->RPort;
  337.     SetAPen(&dataRP,1);
  338.     SetBPen(&dataRP,0);
  339.  
  340.     clrRP = *window->RPort;
  341.     SetAPen(&clrRP,0);
  342.  
  343.     /* renders to the window's RastPort in color 1 */
  344.     Text(&dataRP,"hello",5);
  345.  
  346.     /* clears a section of the window's RastPort in color 0 */
  347.     RectFill(&clrRPort,0,0,10,10);
  348.  
  349.  
  350. Refreshing a Sizable Window
  351.  
  352. A sizable window is by far the trickiest to refresh correctly.  The
  353. most important and often overlooked point is that an application
  354. needs to ensure the size of its window does not change while it is
  355. refreshing the window.  If the application doesn't and the user
  356. changes the window size, the application will refresh the window at
  357. the window's old size rather than its new size.  This can severely
  358. corrupt the appearance of the window.
  359.  
  360. There are two main ways an application can keep the size of its
  361. window stable while refreshing  the window.  The first method is to
  362. keep the size of the window locked most of the time, unlocking the
  363. window only when the user tries to size the window.  The other
  364. approach is to lock the window size only while rendering to the
  365. window.
  366.  
  367. The first method is part of Intuition's IDCMP mechanism.  To lock a
  368. window's size, an application sets the window's IDCMP_SIZEVERIFY
  369. IDCMP flag.  When the user attempts to size the window by clicking on
  370. the window's sizing gadget, Intuition notifies the application by
  371. sending an IDCMP_SIZEVERIFY message to the window's IDCMP port.
  372. Intuition will keep the window's size locked until the application
  373. returns the IDCMP_SIZEVERIFY message.
  374.  
  375. In general this scheme works well, but it does have two problems.
  376. First, if the application is busy doing some processing, such as
  377. recalculating a spreadsheet, it may not notice that the message
  378. arrived until it is done with its current processing.  The result is
  379. the user will not be able to size the window until the application is
  380. finished processing, which might more time than the user wants.
  381.  
  382. The second problem occurs when the application is waiting for input
  383. from Intuition, such as in the middle of an EasyRequest() call.  If
  384. the user clicks on the sizing gadget of the application's window
  385. while the requester is up, Intuition will wait for the system will
  386. enter a deadlock.  Intuition will wait for the application to send
  387. back will not see the IDCMP_SIZEVERIFY event until the user satisfies
  388. the EasyRequest().  The result will be a system deadlock.  Many
  389. applications suffer from this problem.
  390.  
  391. The second problem occurs when the application is waiting for input
  392. from Intuition, such as in the middle of an EasyRequest() call.  If
  393. the user clicks on the sizing gadget of the application's window
  394. while the requester is up, the system will enter a deadlock.  When
  395. the user clicks the sizing gadget, Intuition sends the
  396. IDCMP_SIZEVERIFY message and waits for a reply.  Because the
  397. application is already waiting for the EasyRequest() to return, the
  398. application cannot send back the reply.  Many applications suffer
  399. from this problem.
  400.  
  401. As of Release 2, Intuition has adapted and avoids these deadlocks.
  402. Intuition will time out the sizing operation if the application does
  403. not process the IDCMP_SIZEVERIFY message within a given time period.
  404. Although the user can no longer deadlock the system, this situation
  405. can still confuse the user because clicking on the window's sizing
  406. gadget no longer sizes the window.  That does not mean an application
  407. should rely upon Intuition to avoid the deadlock.  An application
  408. should always avoid these conditions.
  409.  
  410. In general, it is simpler and safer for the application to lock a
  411. window only during the rendering process.  The application can do
  412. this by surrounding all rendering operations with calls to the Layers
  413. library functions LockLayer() and UnlockLayer().  LockLayer() locks
  414. the size and position of a layer.  While a window's layer is locked,
  415. an application can safely look at the window's current size and
  416. render to it without any danger of the size changing.  Once the
  417. application finishes rendering, it unlocks the window's layer by
  418. calling UnlockLayer().  When using this method, an application must
  419. not set the window's IDCMP_SIZEVERIFY bit.  Be careful which system
  420. functions an application calls while it has a layer locked.  Only use
  421. the Graphics library rendering functions and the simple Intuition
  422. rendering functions (i.e., PrintIText(), DrawImage(), etc.).  In
  423. particular, avoid calls that deal with gadgets (including
  424. RefreshGList()) and other locks (i.e., LockIBase() and
  425. LockLayerInfo()).
  426.  
  427.  
  428. BeginRefresh() and EndRefresh()
  429.  
  430. To improve the performance of repairing damage to simple and smart
  431. refresh windows, Intuition can put a window into a special refresh
  432. state using Intuition's BeginRefresh() function:
  433.  
  434.   VOID BeginRefresh(struct Window *window)
  435.  
  436. While a window is in this state, the system restricts attempts to
  437. render into the window to the window's damaged regions.  Because the
  438. system ignores rendering operations outside the window's damage
  439. regions, an application only refreshes the parts of a window that
  440. need refreshing.  This can significant decrease the amount of time
  441. necessary to refresh the window.  This also reduces the possibility
  442. of visual flicker that can happen if an application has to redraw the
  443. entire contents of a window.
  444.  
  445. To end a window's special refresh state, use EndRefresh():
  446.  
  447.   VOID EndRefresh(struct Window *window, BOOL Complete)
  448.  
  449. Compared to BeginRefresh(), EndRefresh() has an extra parameter, a
  450. boolean value.  If this value is TRUE, Intuition assumes that all of
  451. the refreshing for this window is finished and removes all of the
  452. window layer's damaged regions.  In most cases, this value should be
  453. TRUE.  See the Autodoc for BeginRefresh() and EndRefresh() for more
  454. information.
  455.  
  456.  
  457. Backfill Hook
  458.  
  459. Whenever a layer sustains damage, the damaged region is cleared to
  460. color 0.  Clearing the region requires an often lengthy Blitter
  461. operation which is unnecessary if the application plans to redraw the
  462. damaged region anyway.  Such an application can improve its
  463. performance by preventing the Layers library from clearing the
  464. damaged regions.  This is what layer backfill hooks are for.
  465.  
  466. Backfill hooks are a new Layers library feature added in Release 2.
  467. A backfill hook is a custom function that an application attaches to
  468. a specific layer.  Whenever damage occurs to a layer, the Layers
  469. library calls that layer's custom backfill hook.  The Layers library
  470. passes the position and dimensions of the damage area to the backfill
  471. hook.  The backfill hook can render into the damage area, refreshing
  472. it.  Theoretically, the backfill hook can redraw a layer's damaged
  473. regions instead of clearing the damage area to color 0.
  474.  
  475. Unfortunately, refreshing a layer from a backfill hook can be quite
  476. difficult.  The backfill hook code is usually called from a different
  477. task than the main application.  If the backfill hook and the
  478. application do not properly arbitrate access to the application's
  479. data, dangerous race conditions can occur if the hook and the
  480. application try to access the same data.  Another problem with
  481. rendering through the backfill hook is that no clipping is available.
  482. The backfill hook must do its own clipping to ensure that no
  483. rendering goes outside the dimensions specified when the hook is
  484. called, which in itself can be quite difficult.
  485.  
  486. For many situations, the best use of a backfill hook is to have it do
  487. nothing.  Whenever the hook is called, it simply returns without
  488. doing any rendering.  This has the effect of eliminating the extra
  489. blit done to clear the damage region to color 0.  This can speed
  490. layer operations quite a bit.
  491.  
  492. There is one problem with a no-op backfill hook.  If the application
  493. is busy doing some processing and damage occurs to its layer, the
  494. display will remain dirty until the application finishes its
  495. processing and notices that the layer is damaged.  This damage can
  496. include remnants of system imagery, like window borders, which can
  497. confuse the user.
  498.  
  499. One way to overcome this problem is to use a backfill hook that
  500. changes its behavior depending on the state of the application.  If
  501. the application is not too busy to notice damage to its window's
  502. layer, the backfill hook does not erase the display.  However, if the
  503. application is too busy to refresh its damaged window, the backfill
  504. hook clears the damaged portion of the display.  Using this method,
  505. the backfill hook will not waste time erasing the damage when the
  506. application will update it immediately, but the backfill hook will
  507. erase the damage if the application can't refresh for a while.
  508.